#include "scripting/scheduler_generator.h"
#include "scheduler/scheduler_factory.h"
#include "scheduler/sequence_scheduler.h"
#include "scheduler/inverter_scheduler.h"
#include "scheduler/fallback_scheduler.h"

namespace script
{
class SchedulerGeneratorData
{
private:
    friend class SchedulerGenerator;
    std::shared_ptr<ScriptTaskObjectPool> task_pool;
    std::string last_error_message;

private:
    void setErrorMessage(const std::string &msg);

    std::optional<scheduler::AbstractSchedulerConstPtr> generateExpressionScheduler(const ASTNode &ast_node);

    std::optional<scheduler::AbstractSchedulerConstPtr> generateNotScheduler(const script::ASTNode &ast_node);

    std::optional<scheduler::AbstractSchedulerConstPtr> generateAndScheduler(const script::ASTNode &ast_node);
    bool generateAndScheduler(const script::ASTNode &ast_node, std::shared_ptr<scheduler::SequenceScheduler> &sequence_scheduler);

    std::optional<scheduler::AbstractSchedulerConstPtr> generateOrScheduler(const script::ASTNode &ast_node);
    bool generateOrScheduler(const script::ASTNode &ast_node, std::shared_ptr<scheduler::FallbackScheduler> &fallback_scheduler);
};
void SchedulerGeneratorData::setErrorMessage(const std::string &msg)
{
    if (last_error_message.empty())
    {
        last_error_message = msg;
    }
}
std::optional<scheduler::AbstractSchedulerConstPtr> SchedulerGeneratorData::generateExpressionScheduler(const ASTNode &ast_node)
{
    //表达式node type只有四种，and/or/not/identifier
    //and只有0个或者1个
    std::optional<scheduler::AbstractSchedulerConstPtr> scheduler;
    for (const auto &node: ast_node.children)
    {
        switch (node.type)
        {
            case ASTNodeType::AND:
                scheduler = generateAndScheduler(node);
                break;
            case ASTNodeType::OR:
                scheduler = generateOrScheduler(node);
                break;
            case ASTNodeType::NOT:
                scheduler = generateNotScheduler(node);
                break;
            default:
                setErrorMessage("generate expression scheduler fail, unknown ast node type");
                break;
        }
    }
    return scheduler;
}
std::optional<scheduler::AbstractSchedulerConstPtr> SchedulerGeneratorData::generateNotScheduler(const script::ASTNode &ast_node)
{
    if (ast_node.children.size() != 1)
    {
        setErrorMessage("expected not operate expression, no content");
        return std::nullopt;
    }
    ASTNode node = ast_node.children.at(0);
    auto inverter_scheduler = scheduler::SchedulerFactory<scheduler::InverterScheduler>::createScheduler("inverter_scheduler");
    if (script::ASTNodeType::Expression == node.type)
    {
        auto scheduler = generateExpressionScheduler(node);
        if (scheduler)
        {
            inverter_scheduler->setScheduler(scheduler.value());
        }
    }
    else if (script::ASTNodeType::Identifier == node.type)
    {
        auto task = task_pool->getTaskObject(node.value);
        if (task)
        {
            inverter_scheduler->setTask(task.value());
        }
    }
    else
    {
        setErrorMessage("expected not operate expression, unknown node type");
        return std::nullopt;
    }
    return inverter_scheduler;
}
std::optional<scheduler::AbstractSchedulerConstPtr> SchedulerGeneratorData::generateAndScheduler(const script::ASTNode &ast_node)
{
    auto sequence_scheduler = scheduler::SchedulerFactory<scheduler::SequenceScheduler>::createScheduler("sequence_scheduler");
    for (const auto &node: ast_node.children)
    {
        if (!generateAndScheduler(node, sequence_scheduler))
        {
            return std::nullopt;
        }
    }
    return sequence_scheduler;
}
bool SchedulerGeneratorData::generateAndScheduler(const ASTNode &ast_node, std::shared_ptr<scheduler::SequenceScheduler> &sequence_scheduler)
{
    bool res = false;
    switch (ast_node.type)
    {
        case ASTNodeType::Expression:
        {
            auto scheduler = generateExpressionScheduler(ast_node);
            if (scheduler)
            {
                res = true;
                sequence_scheduler->appendScheduler(scheduler.value());
            }
        }
            break;
        case ASTNodeType::Identifier:
        {
            auto task = task_pool->getTaskObject(ast_node.value);
            if (task)
            {
                res = true;
                sequence_scheduler->appendTask(task.value());
            }
        }
            break;
        case ASTNodeType::NOT:
        {
            auto scheduler = generateNotScheduler(ast_node);
            if (scheduler)
            {
                res = true;
                sequence_scheduler->appendScheduler(scheduler.value());
            }
        }
            break;
        default:
            setErrorMessage("expected and operate expression, unknown node type");
            break;
    }
    return res;
}
std::optional<scheduler::AbstractSchedulerConstPtr> SchedulerGeneratorData::generateOrScheduler(const ASTNode &ast_node)
{
    auto fallback_scheduler = scheduler::SchedulerFactory<scheduler::FallbackScheduler>::createScheduler("fallback_scheduler");
    for (const auto &node: ast_node.children)
    {
        if (!generateOrScheduler(node, fallback_scheduler))
        {
            return std::nullopt;
        }
    }
    return fallback_scheduler;
}
bool SchedulerGeneratorData::generateOrScheduler(const ASTNode &ast_node, std::shared_ptr<scheduler::FallbackScheduler> &fallback_scheduler)
{
    bool res = false;
    switch (ast_node.type)
    {
        case ASTNodeType::Expression:
        {
            auto scheduler = generateExpressionScheduler(ast_node);
            if (scheduler)
            {
                res = true;
                fallback_scheduler->appendScheduler(scheduler.value());
            }
        }
            break;
        case ASTNodeType::NOT:
        {
            auto scheduler = generateNotScheduler(ast_node);
            if (scheduler)
            {
                res = true;
                fallback_scheduler->appendScheduler(scheduler.value());
            }
        }
            break;
        case ASTNodeType::AND:
        {
            auto scheduler = generateAndScheduler(ast_node);
            if (scheduler)
            {
                res = true;
                fallback_scheduler->appendScheduler(scheduler.value());
            }
        }
            break;
        case ASTNodeType::Identifier:
        {
            auto task = task_pool->getTaskObject(ast_node.value);
            if (task)
            {
                res = true;
                fallback_scheduler->appendTask(task.value());
            }
        }
            break;
        default:
            setErrorMessage("expected or operate expression, unknown node type");
            break;
    }
    return res;
}
}

namespace script
{
SchedulerGenerator::SchedulerGenerator(const std::shared_ptr<ScriptTaskObjectPool> &task_pool)
    : d(std::make_unique<SchedulerGeneratorData>())
{
    d->task_pool = task_pool;
}
SchedulerGenerator::~SchedulerGenerator() = default;

std::optional<scheduler::AbstractSchedulerConstPtr> SchedulerGenerator::generateExpressionScheduler(const ASTNode &ast_node)
{
    if (ast_node.children.empty())
    {
        d->setErrorMessage("expected empty expression");
        return std::nullopt;
    }
    return d->generateExpressionScheduler(ast_node);
}

}
